import os
import socket
import time
from multiprocessing import Process


#打印

def is_path_a_file(path):
    """
    判断给定的路径是否指向一个文件

    参数:
    path (str): 要检查的路径

    返回:
    bool: 如果路径指向一个文件，返回 True；否则返回 False
    """
    # 检查路径是否包含至少一个'/'和一个'.'
    if path.count('/') < 1 or path.count('.') < 1:
        return False

    # 找到最后一个'/'的位置
    last_slash_index = path.rfind('/')

    # 找到最后一个'.'的位置
    last_dot_index = path.rfind('.')

    # 检查最后一个'.'是否在最后一个'/'之后
    if last_slash_index == -1 or last_dot_index <= last_slash_index:
        return False

    # 如果最后一个'.'在字符串末尾，那么它不是一个文件
    if last_dot_index == len(path) - 1:
        return False

    # 检查路径的最后一部分是否以'.'开头
    if path[last_slash_index + 1].startswith('.'):
        return False

    return True


# 定义一个函数，用于获取指定路径下所有文件和文件夹的信息
def get_file_info(path):
    files_and_folders = []
    if not is_path_a_file(path):
        for f in os.listdir(path):  # 遍历指定路径下的所有文件和文件夹
            full_path = os.path.join(path, f)  # 获取完整的文件或文件夹路径
            if os.path.isdir(full_path):  # 如果是文件夹
                files_and_folders.append((f"{f}/", 0))  # 文件夹大小为0，添加到列表中
            elif os.path.isfile(full_path):  # 如果是文件
                size_mb = os.path.getsize(full_path) / (1024 * 1024)  # 转换为MB
                files_and_folders.append((f, size_mb))  # 添加文件名和大小到列表中
        return files_and_folders  # 返回文件和文件夹信息列表
    else:
        size_mb = os.path.getsize(path) / (1024 * 1024)  # 转换为MB
        files_and_folders.append((path, size_mb))  # 添加文件名和大小到列表中
        print(files_and_folders)
        return files_and_folders


# 定义一个函数，用于发送文件给客户端
def send_file(client_socket, file_path):
    print(client_socket, file_path)
    #return None
    file_size = os.path.getsize(file_path)  # 获取文件大小
    with open(file_path, 'rb') as file:  # 以二进制读取模式打开文件
        while True:
            data = file.read(1024*1024)  # 读取1024字节数据
            if not data:  # 如果没有数据，表示文件已发送完毕
                break
            client_socket.sendall(data)  # 发送数据给客户端
    client_socket.sendall(
        f"File {os.path.basename(file_path)} sent. Size: {file_size} bytes".encode('gbk'))  # 发送文件发送完毕的消息
    print('发送完成')


def UpdataPath(base_path, client_socket,response):
    """
    更新基路径并发送当前目录下的文件和文件夹信息给客户端

    参数:
    base_path (str): 当前的基路径
    client_socket (socket.socket): 与客户端通信的socket对象

    返回:
    None
    """
    # 更新基路径为上一级目录
    base_path = os.path.dirname(base_path)
    # 获取更新后的基路径下的文件和文件夹信息
    files_and_folders_info = get_file_info(base_path)
    print("获取文件夹数据成功")
    if response == "../":  # 如果客户端请求切换到上一级目录
        files_and_folders_info=[('切换上级目录',0)]
    # 发送文件和文件夹信息给客户端
    client_socket.sendall(str(files_and_folders_info).encode('gbk'))
    #print('files_and_folders_info:', files_and_folders_info)
    print('发送文件夹数据成功，长度为:', files_and_folders_info.__len__())


def Parse_client_data(response, base_path):
    """
        解析客户端的响应

        参数:
        response (str): 客户端的响应
        base_path (str): 当前的基路径

        返回:
        list: 解析后的响应列表
        """

    files_and_folders_info = get_file_info(base_path)  # 获取当前路径下的文件和文件夹信息
    fa_path = os.path.dirname(base_path)# 上一级目录
    try:
        response = int(response)  # 尝试将响应转换为整数索引
    except ValueError:  # 如果转换失败，表示不是整数索引,是命令
        return False, response,base_path,fa_path
        pass
    if type(response) == str:
        return False, response,base_path,fa_path
    elif type(response) == int:
        index = int(response)  # 尝试将响应转换为整数索引
        try:# 越界部分还没处理好
            selected = [name for name, size in files_and_folders_info][index]  # 获取选中的文件或文件夹名称
            selected_path = os.path.join(base_path, selected.rstrip('/'))  # 获取选中的文件或文件夹路径
            #print('selected_path:', selected_path)
            return True, int(response), selected_path,fa_path
        except:
            return False, response, base_path, fa_path


# 定义一个函数，用于处理客户端的请求
def handle_client(client_socket, base_path,data_size,addr):
    ml=['wq','ls','../','pwd']
    ml.extend([str(i) for i in range(0, 2122)])
    print(f"Connected by {addr} time:{time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())}")  # 打印客户端连接信息
    while True:
        response = client_socket.recv(data_size).decode('gbk')  # 接收客户端的响应
        if response not in ml:  # 如果客户端发送未知数据
            files_and_folders_info = [('未知', 0)]
            # 发送提示给客户端
            client_socket.sendall(str(files_and_folders_info).encode('gbk'))

        is_order, response, selected_path,fa_path = Parse_client_data(response, base_path)  # 解析客户端的响应
        base_path = selected_path  # 更新基路径
        print(f'addr:{addr},response:{response},type:{type(response)},selected_path:{selected_path},is_order:{is_order}',end=' ')
        if response == "":  # 如果客户端异常
            print("exit")
            break
        if not is_order:  # 是指令
            if response == "ls":  # 如果客户端请求列出文件和文件夹
                print("命令为ls")
                files_and_folders_info = get_file_info(base_path)  # 获取当前路径下的文件和文件夹信息
                client_socket.sendall(str(files_and_folders_info).encode('gbk'))  # 发送文件和文件夹信息给客户端
                continue
            elif response == "../":  # 如果客户端请求切换到上一级目录
                print("切换到上一级目录")#要截断路径后面
                base_path=fa_path
                UpdataPath(fa_path, client_socket,response)  # 切换到上一级目录
                continue
            elif response =="pwd":#如果客户端请求打印当前路径
                print("打印当前路径")
                data=[(base_path,0)]
                client_socket.sendall(str(data).encode('gbk'))  # 发送当前路径给客户端
                continue
            elif response == "wq":  # 如果客户端请求退出
                print("退出")
                client_socket.sendall("exit".encode('gbk'))  # 发送退出消息给客户端
                break
        else:  # 不是指令,是文件或文件夹的地址
            if not is_path_a_file(selected_path):  # 如果客户端选中的地址是文件夹
                print(f"进入文件夹:{selected_path}")
                UpdataPath(selected_path, client_socket,response)  # 发送文件夹信息给客户端
                continue
            elif is_path_a_file(selected_path):  # 如果客户端选中的地址是文件
                print(f"下载文件:{selected_path}")
                files_and_folders_info = [(selected_path, 0)]  # 创建文件信息列表
                client_socket.sendall(str(files_and_folders_info).encode('gbk'))
                send_file(client_socket, selected_path)  # 发送文件给客户端
                continue
            else:
                print("出现错误")
                client_socket.sendall("Invalid input".encode('gbk'))  # 发送错误消息给客户端
                break


# 定义一个函数，用于启动服务器
def start_server(host, port, base_path, data_size):
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  # 创建socket对象
    server_socket.bind((host, port))  # 绑定主机和端口
    server_socket.listen()  # 监听连接

    while True:
        client_socket, addr = server_socket.accept()  # 接受客户端连接
        handle_client(client_socket, base_path, data_size,addr)
        # 创建一个新的进程来处理客户端请求
        #client_process = Process(target=handle_client, args=(client_socket, base_path, data_size,addr))
        #client_process.start()  # 启动进程
        #client_process.join()  # 等待进程结束
        #client_socket.close()  # 关闭客户端连接


if __name__ == "__main__":
    global data_size
    data_size = 1024 * 1024
    port = 2121  # 设置端口号
    base_path = r"F:/"  # 设置文件路径
    host='127.0.0.1'
    print("开放端口为：", port, "映射地址为：", base_path)  # 打印端口和路径信息
    start_server(host, port, base_path,data_size)  # 启动服务器
